package ecBase;
import java.util.*;

/*
 * Evolution.java
 * Created: 2005-12-14 20:20
 */
/**
 * This class contains methods for evolution:
 *  - Selection
 *  - Mutation/Recombination
 *  - Replacement
 *
 * @author T.S.Yo
 * @last updated 2005/12/14
**/

public class Evolution{
    private int selOpt;    // Option indicates the Selection method
    private int evoOpt;    // Option indicates evolving through mutation (1) or recombination (2)
    private int mutOpt;    // Option indicates the Mutation method
    private int recOpt;    // Option indicates the Recombination method
    private int repOpt;    // Option indicates the Replacement method
// Constructor    
    public Evolution(final int evo, final int sel,final int mut,
                     final int rec, final int rep){
        evoOpt = evo;
        selOpt = sel;
        mutOpt = mut;
        recOpt = rec;
        repOpt = rep;        
    }
    
// Selection
    /** Selecting the population by given option **/
    public Population selection(final Population pop){
        Random rand = new Random();
        switch(selOpt){
            case 1:  // Porportion Selection
                Population newPop = propSelection(pop);
                break;
            case 2:  // Tournament Selection
                Population newPop = tourSelection(pop);
                break;
            default: // Return whole population
                Population newPop = new Population(pop);
                break;
        }
        return newPop;
    }

// Choose method to generate new individuals    /** Evolving the population by given option **/
    public Population selection(final Population pop){
        Random rand = new Random();
        switch(evoOpt){
            case 1:  // Mutation
                mutation(pop);
                break;
            case 2:  // Recombination
                recombination(pop);
                break;
            default: // Do nothing
                break;
        }
    }
}
//
//
//
//// Defining Evolution    /** Defining the options for evolution **/    // evoOpt1: 1. mutation
    //                     (1) Randomly generate a new one
    //                     Default: return the original one
    //               2. reproduction
    //                     (1) Uniform crossover
    //                     Default: 1 point crossover

    /** Evolving the population by given option **/
    public void evolve(final int evoOpt1, final int evoOpt2){
        Random rand = new Random();
        switch(evoOpt1){
            case 1:
                int indIndex = rand.nextInt(popSize);    // Index of individual to mutate
                //System.out.println("Evolve option: Mutation  "+indIndex);
                Individual tmpInd = population[indIndex];
                Individual mutInd = mutation(evoOpt2,tmpInd);
                this.sortPopulation();
                //if (this.population[0].compareTo(mutInd) < 0){
                    this.population[0].setGenes(mutInd);
                //}
                break;
            case 2:
                int iP1 = rand.nextInt(popSize);  // Index of parent1
                int iP2 = rand.nextInt(popSize);  // Index of parent2
                //System.out.println("Evolve option: Reproduction  "+iP1+"  "+iP2);
                //System.out.println("    Parent 1  "+population[iP1]);
                //System.out.println("    Parent 2  "+population[iP2]);
                Individual parent1 = population[iP1];
                Individual parent2 = population[iP2];
                Individual repInd = reproduction(evoOpt2, parent1, parent2);
                this.sortPopulation();
                //if (this.population[0].compareTo(repInd) < 0){
                    this.population[0].setGenes(repInd);
                //}
                break;
            default:       
                System.out.println("Please choose 1-mutation, or 2-reproduction");
                break;
        }
    }
//// Generating new individuals    /** Mutating from given individual **/
    public Individual mutation(final int mutOpt, Individual ind){
        Individual mutInd;
        switch(mutOpt){
            case 1:
                //System.out.println("Regenerate a random individual");
                mutInd = new Individual(ind.getArraySize());
                break;
            default:       
                //System.out.println("Copy given individual");
                mutInd = new Individual(ind);
                break;
        }
        return mutInd;
    }

    /** Reproducing an individual from 2 given individuals **/
    public Individual reproduction(final int repOpt, 
                        final Individual ind1, final Individual ind2)
    {
        int genoSize = ind1.getArraySize();
        Individual repInd = new Individual(genoSize);
        // Check exception
        if(genoSize!=ind2.getArraySize()){
            System.out.println("Parents with different length, return a random Individual");
        } else {    
            Random rand = new Random();
            switch(repOpt){
                case 1:
                    //System.out.println("Uniform Crossover");
                    for (int i = 0; i < genoSize; i++){
                        int geneFrom = rand.nextInt(2);
                        //System.out.println("");
                        //System.out.print("geneFrom "+geneFrom);
                        switch(geneFrom){
                          case 0:
                            //System.out.print("  "+ind1.getGenes()[i]);
                            repInd.setGene(i,ind1.getGenes()[i]);
                            break;
                          default:
                            //System.out.print("  "+ind2.getGenes()[i]);
                            repInd.setGene(i,ind2.getGenes()[i]);
                            break;
                        }
                    }
                    //System.out.println("");
                    //System.out.println("    Offspring: "+repInd);
                    break;
                default:       
                    //System.out.println("1 Point Crossover");
                    int cutPoint = rand.nextInt(genoSize);
                    for (int i = 0; i < cutPoint; i++){
                        repInd.setGene(i,ind1.getGenes()[i]);
                    }
                    for (int i = cutPoint; i < genoSize; i++){
                        repInd.setGene(i,ind2.getGenes()[i]);
                    }
                    break;
            }
        }
        return repInd;
    }
